﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Mono.Cecil;
using Mono.Cecil.Pdb;
using Mono.Collections.Generic;

namespace MakeSerializable
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var _args = new Arguments(args);
                var path = _args.Single("assembly");
                var matchPattern = _args.Single("match");
                if (_args.IsTrue("cancel"))
                {
                    Console.WriteLine("Program has been canceled");
                    return;
                }
                if (string.IsNullOrEmpty(path))
                {
                    Console.WriteLine("Please specify assembly path.");
                    Console.WriteLine(@"
Usage:MakeSerializable -assembly=assemblyFilePath [-prj=projectFilePath] [-key=keyFilePath] [-match=RegexPattern]
Example:MakeSerializable -assembly='c:\test.dll' -prj='c:\test.csproj'
     or:MakeSerializable -assembly='c:\test.dll' -key='c:\test.snk'
");
                    return;
                }
                StrongNameKeyPair strongNameKeyPair = null;
                var keyPath = _args.Single("key");
                var projectPath = _args.Single("prj");
                if (!string.IsNullOrWhiteSpace(keyPath))
                {
                    Console.WriteLine("Reading key file:" + keyPath);
                    strongNameKeyPair = new StrongNameKeyPair(File.OpenRead(keyPath));
                }
                else if (!string.IsNullOrWhiteSpace(projectPath))
                {
                    Console.WriteLine("Reading key from project file:" + projectPath);
                    var prjReader = new ProjectKeyReader();
                    prjReader.Execute(projectPath);
                    strongNameKeyPair = prjReader.StrongNameKeyPair;
                }
                ProcessAssembly(path, strongNameKeyPair, matchPattern);
            }
            catch (Exception ex)
            {
                //Console.WriteLine(path);
                Console.WriteLine(ex.Message);
                return;
            }

            Console.WriteLine("Make Serializable Succeeded.");
        }

        private static void ProcessAssembly(string assemblyPath, StrongNameKeyPair strongNameKeyPair, string matchPattern)
        {
            var assemblyResolver = new DefaultAssemblyResolver();
            var assemblyLocation = Path.GetDirectoryName(assemblyPath);
            assemblyResolver.AddSearchDirectory(assemblyLocation);
            var silverlightAssemblyPath =
                Environment.ExpandEnvironmentVariables(@"%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.5\");
            assemblyResolver.AddSearchDirectory(silverlightAssemblyPath);
            var readerParameters = new ReaderParameters { AssemblyResolver = assemblyResolver };
            var writerParameters = new WriterParameters();
            var pdbName = Path.ChangeExtension(assemblyPath, "pdb");
            if (File.Exists(pdbName))
            {
                var symbolReaderProvider = new PdbReaderProvider();
                readerParameters.SymbolReaderProvider = symbolReaderProvider;
                readerParameters.ReadSymbols = true;
                writerParameters.WriteSymbols = true;
            }

            if (strongNameKeyPair != null)
            {
                writerParameters.StrongNameKeyPair = strongNameKeyPair;
            }
            var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath, readerParameters);

            var types = assemblyDefinition.MainModule.Types;
            Regex typPattern = null;
            if (!string.IsNullOrWhiteSpace(matchPattern))
            {
                typPattern = new Regex(matchPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            }

            foreach (TypeDefinition type in types)
            {
                bool needChange = false;
                foreach (var attr in type.CustomAttributes)
                {
                    if (attr.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
                    {
                        Console.WriteLine("found type:" + type.FullName);
                        needChange = true;
                        break;
                    }
                }
                if (!needChange)
                {
                    if (typPattern != null && typPattern.IsMatch(type.FullName))
                    {
                        Console.WriteLine("match type:" + type.FullName);
                    }
                }

                if (needChange)
                {
                    type.IsPublic = true;
                    type.IsSerializable = true;
                }
                var nestedTypes = type.NestedTypes;
                MakeSeriaziable(nestedTypes, typPattern);
            }
            assemblyDefinition.Write(assemblyPath, writerParameters);
        }

        private static void MakeSeriaziable(IEnumerable<TypeDefinition> nestedTypes, Regex matchPattern)
        {
            if (nestedTypes == null)
            {
                return;
            }
            foreach (var ntype in nestedTypes)
            {
                bool needChange = false;
                foreach (var attr in ntype.CustomAttributes)
                {
                    if (attr.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
                    {
                        needChange = true;
                        break;
                    }
                }

                if (!needChange && matchPattern != null)
                {
                    if (matchPattern.IsMatch(ntype.FullName))
                    {
                        needChange = true;
                    }
                }

                if (needChange)
                {
                    Console.WriteLine("found type:" + ntype.FullName);
                    ntype.IsSealed = false;
                    ntype.IsSerializable = true;
                    ntype.IsNestedPublic = true;
                }
                MakeSeriaziable(ntype.NestedTypes, matchPattern);
            }
        }
    }






}
